package com.yeskery.nut.extend.jdbc;

import com.yeskery.nut.util.JdbcUtils;
import com.yeskery.nut.util.ReflectUtils;
import com.yeskery.nut.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * jdbc result转换器
 * @author YESKERY
 * 2024/10/10
 */
public class JdbcResultConverter {

    /** 实例对象 */
    private static final JdbcResultConverter INSTANCE = new JdbcResultConverter();

    /** 私有化构造方法 */
    private JdbcResultConverter(){
    }

    /**
     * 获取实例对象
     * @return 实例对象
     */
    public static JdbcResultConverter getInstance() {
        return INSTANCE;
    }

    /**
     * 创建实例对象
     * @param clazz 目标对象类型
     * @return 实例对象
     * @param <T> 目标对象类型
     */
    public <T> T createObject(Class<T> clazz) {
        return ReflectUtils.getNoConstructorTarget(clazz);
    }

    /**
     * 获取设置字段集合
     * @param columnNameStrategy 列名策略
     * @param clazz 查询对象类型
     * @return 设置字段集合
     */
    public Map<String, Field> getSetFieldMap(ColumnNameStrategy columnNameStrategy, Class<?> clazz) {
        Set<Field> setFields = getSetFields(clazz);
        Map<String, Field> setFieldMap = new HashMap<>(setFields.size());
        for (Field setField : setFields) {
            setFieldMap.put(columnNameStrategy.getColumnName(setField.getName()), setField);
        }
        return setFieldMap;
    }

    /**
     * 获取设置的所有字段集合
     * @param clazz 类型
     * @return 设置的所有字段集合
     */
    public Set<Field> getSetFields(Class<?> clazz) {
        return Arrays.stream(ReflectUtils.getBeanAllField(clazz))
                .filter(f -> !Modifier.isStatic(f.getModifiers()))
                .collect(Collectors.toSet());
    }

    /**
     * 获取设置方法集合
     * @param columnNameStrategy 列名策略
     * @param clazz 查询对象类型
     * @return 设置方法集合
     */
    public Map<String, Method> getSetMethodMap(ColumnNameStrategy columnNameStrategy, Class<?> clazz) {
        Set<Method> setMethods = getSetMethods(clazz);
        Map<String, Method> setMethodMap = new HashMap<>(setMethods.size());
        for (Method setMethod : setMethods) {
            String fieldName = setMethod.getName().substring(3);
            fieldName = fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1);
            setMethodMap.put(columnNameStrategy.getColumnName(fieldName), setMethod);
        }
        return setMethodMap;
    }

    /**
     * 获取设置的所有方法集合
     * @param clazz 类型
     * @return 设置的所有方法集合
     */
    public Set<Method> getSetMethods(Class<?> clazz) {
        return Arrays.stream(ReflectUtils.getBeanMethods(clazz))
                .filter(m -> !Modifier.isStatic(m.getModifiers()))
                .filter(m -> m.getName().startsWith("set") && m.getName().length() > 3 && m.getParameterCount() == 1)
                .collect(Collectors.toSet());
    }

    /**
     * 从结果集填充值到map中
     * @param resultSet 结果集对象
     * @param resultSetMetaData 结果集元数据对象
     * @param map 结果集映射对象
     * @throws SQLException SQLException
     */
    public void appendMapValueFromResultSet(ResultSet resultSet, ResultSetMetaData resultSetMetaData, Map<String, Object> map) throws SQLException {
        for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
            String name = resultSetMetaData.getColumnLabel(i);
            if (StringUtils.isEmpty(name)) {
                name = resultSetMetaData.getColumnName(i);
            }
            map.putIfAbsent(name, JdbcUtils.getResultSetValue(resultSet, i));
        }
    }

    /**
     * 从结果集填充值到对象中
     * @param resultSet 结果集对象
     * @param resultSetMetaData 结果集元数据对象
     * @param setMethodMap set方法map
     * @param object 目标对象
     * @throws SQLException SQLException
     */
    public void appendObjectFieldFromResultSet(ResultSet resultSet, ResultSetMetaData resultSetMetaData, Map<String, Method> setMethodMap, Object object) throws SQLException {
        for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
            String name = resultSetMetaData.getColumnLabel(i);
            if (StringUtils.isEmpty(name)) {
                name = resultSetMetaData.getColumnName(i);
            }
            if (setMethodMap.containsKey(name)) {
                Method method = setMethodMap.get(name);
                Object value = JdbcUtils.getResultSetValue(resultSet, i, method.getParameterTypes()[0]);
                try {
                    method.invoke(object, value);
                } catch (Exception e) {
                    throw new DataAccessException("Class[" + object.getClass().getName() + "] Method[" + method.getName() + "] Invoke Fail.", e);
                }
            }
        }
    }
}